=begin rdoc
	Contains methods to interact with the Windows API.
=end

=begin
	Data Types: http://msdn.microsoft.com/en-us/library/aa383751(VS.85).aspx
	
	typedef unsigned long       DWORD;		# 4
	
	// wc,   16-bit UNICODE character
	typedef wchar_t WCHAR;    				# 2
		Under Win32, wchar_t is 16 bits wide and represents a UTF-16 code unit.
		On Unix-like systems wchar_t is commonly 32 bits wide and represents a UTF-32 code unit.
	
	typedef struct _SYSTEMTIME {
		WORD wYear;								#  2
		WORD wMonth;							#  2
		WORD wDayOfWeek;						#  2
		WORD wDay;								#  2
		WORD wHour;								#  2
		WORD wMinute;							#  2
		WORD wSecond;							#  2
		WORD wMilliseconds;						#  2
	} SYSTEMTIME, *PSYSTEMTIME, *LPSYSTEMTIME;	# 16

	typedef struct _FILETIME {
		DWORD dwLowDateTime;				# 4
		DWORD dwHighDateTime;				# 4
	} FILETIME, *PFILETIME, *LPFILETIME;	# 8
	
	typedef struct _WIN32_FIND_DATAW {
		DWORD dwFileAttributes;									#   4
		FILETIME ftCreationTime;								#   8
		FILETIME ftLastAccessTime;								#   8
		FILETIME ftLastWriteTime;								#   8
		DWORD nFileSizeHigh;									#   4
		DWORD nFileSizeLow;										#   4
		DWORD dwReserved0;										#   4
		DWORD dwReserved1;										#   4
		WCHAR  cFileName[ MAX_PATH ];							# 520
		WCHAR  cAlternateFileName[ 14 ];						#  28
	} WIN32_FIND_DATAW, *PWIN32_FIND_DATAW, *LPWIN32_FIND_DATAW;# 592

=end

require 'UnicodeEx\UnicodeEx.rb'

# Contains methods to interact with the Windows API.
module WinEx

	# Flag that's +true+ when running under Windows, +false+ otherwise.
	IS_PC = (Object::RUBY_PLATFORM =~ /mswin/i) ? true : false
	if IS_PC

	require 'Win32API'
	
	# --- Win32 API CONSTANTS --- #	
	
	# UTF-8 Code Page
	CP_UTF8 = 65001
	# -1
	INVALID_HANDLE_VALUE = -1
	# Maximum number of characters in a file name
	MAX_PATH = 260
	# Maxium value of a DWORD
	MAXDWORD = 0xffffffff
	
	FILE_ATTRIBUTE_READONLY				= 0x00000001  
	FILE_ATTRIBUTE_HIDDEN				= 0x00000002  
	FILE_ATTRIBUTE_SYSTEM				= 0x00000004  
	FILE_ATTRIBUTE_DIRECTORY			= 0x00000010  
	FILE_ATTRIBUTE_ARCHIVE				= 0x00000020  
	FILE_ATTRIBUTE_DEVICE				= 0x00000040  
	FILE_ATTRIBUTE_NORMAL				= 0x00000080  
	FILE_ATTRIBUTE_TEMPORARY			= 0x00000100  
	FILE_ATTRIBUTE_SPARSE_FILE			= 0x00000200  
	FILE_ATTRIBUTE_REPARSE_POINT		= 0x00000400  
	FILE_ATTRIBUTE_COMPRESSED			= 0x00000800  
	FILE_ATTRIBUTE_OFFLINE				= 0x00001000  
	FILE_ATTRIBUTE_NOT_CONTENT_INDEXED	= 0x00002000  
	FILE_ATTRIBUTE_ENCRYPTED			= 0x00004000  
	FILE_ATTRIBUTE_VIRTUAL				= 0x00010000  


	# Classes that are wrappers for the Windows API's structs are subclasses of +String+. They are by
	# default +NULL+ padded strings in the size of the struct they represent and contains methods
	# that return the struct data in Ruby objects.
	module Struct
	
	# <tt>typedef struct _SYSTEMTIME</tt>
	#
	# Wrapper for the +SYSTEMTIME+ struct.
	class SYSTEMTIME < String
		# If +string_data+ is not provided, a +NULL+ padded string buffer with the size of the
		# struct is returned.
		def initialize(string = "\000" * 16)
			super(string) #if string.is_a?(String)
		end
		# Returns an +Array+ of the data extracted from the data string.
		def to_a
			return self.unpack('SSSSSSSS')
		end
		# Returns an +Integer+ representing the year.
		def year
			return self.to_a[0]
		end
		# Returns an +Integer+ representing the month.
		def month
			return self.to_a[1]
		end
		# Returns an +Integer+ representing the day of the week.
		def day_of_week
			return self.to_a[2]
		end
		# Returns an +Integer+ representing the day.
		def day
			return self.to_a[3]
		end
		# Returns an +Integer+ representing the hour.
		def hour
			return self.to_a[4]
		end
		# Returns an +Integer+ representing the minute.
		def minute
			return self.to_a[5]
		end
		# Returns an +Integer+ representing the second.
		def second
			return self.to_a[6]
		end
		# Returns an +Integer+ representing the milliseconds.
		def milliseconds
			return self.to_a[7]
		end
		# Returns a +Time+ object.
		def time
			year, month, day_of_week, day, hour, minute, second, millisecond = self.to_a
			return Time.utc(year, month, day, hour, minute, second)
		end
	end
	
	# <tt>typedef struct _FILETIME</tt>
	#
	# Wrapper for the +FILETIME+ struct.
	class FILETIME < String
		# If +string_data+ is not provided, a +NULL+ padded string buffer with the size of the
		# struct is returned.
		def initialize(string_data = "\000" * 8)
			super(string_data)
		end
		# Returns an +Array+ of the data extracted from the data string.
		def to_a
			return self.unpack('LL')
		end
		
		# Returns a +Time+ object.
		def time
			fileTimeToSystemTime = Win32API.new('Kernel32','FileTimeToSystemTime',['P', 'P'], 'I')
			system_time = SYSTEMTIME.new()
			fileTimeToSystemTime.call(self, system_time)
			return system_time.time
		end
	end
	
	# <tt>typedef struct _WIN32_FIND_DATAW</tt>
	#
	# Wrapper for the +WIN32_FIND_DATAW+ struct.
	class WIN32_FIND_DATA < String
		# If +string_data+ is not provided, a +NULL+ padded string buffer with the size of the
		# struct is returned.
		def initialize(string = "\000" * 592)
			super(string) #if string.is_a?(String)
		end
		# Returns an +Array+ of the data extracted from the data string. 
		def to_a
			array = self.unpack('La8a8a8LLLLa520a28')
			array[1] = FILETIME.new(array[1])
			array[2] = FILETIME.new(array[2])
			array[3] = FILETIME.new(array[3])
			return array
		end
		
		# Returns an +Integer+ with the file attributes. Use the +FILE_ATTRIBUTE_?+
		# constants to check the various bit properies.
		def file_attributes
			return self.to_a[0]
		end
		# Returns a +Time+ object.
		def creation_time
			return self.to_a[1].time
		end
		# Returns a +Time+ object.
		def last_access_time
			return self.to_a[2].time
		end
		# Returns a +Time+ object.
		def last_write_time
			return self.to_a[3].time
		end
		# Returns an +Integer+ represetning the file size.
		def file_size
			a = self.to_a
			nFileSizeHigh = a[4]
			nFileSizeLow  = a[5]
			return (nFileSizeHigh * (MAXDWORD + 1)) + nFileSizeLow
		end
		# Returns an UTF-16 +String+ with the file name.
		def file_name
			return self.to_a[8]
		end
		# Returns a +String+ with DOS style (8+3) file name.
		def dos_file_name
			return self.to_a[9]
		end
	end
	
	end # module Struct

	# Collection of methods that call the Windows API's file Unicode functions.
	module FileSystem
		# Returns an WIN32_FIND_DATA object on success or +nil+ on failure.
		# * +file_name+ --- UTF-16 +String+ +NULL+ terminated
		def self.get_file_data(file_name)
			# Declare API calls
			findFirstFile			= Win32API.new('Kernel32','FindFirstFileW',['P','P'], 'I')
			findClose				= Win32API.new('Kernel32','FindClose',['I'], 'I')
			# Reserve memory for the WIN32_FIND_DATA struct
			win32_find_data = WinEx::Struct::WIN32_FIND_DATA.new()
			# Make the calls - remember to close
			handle = findFirstFile.call(file_name, win32_find_data)
			bool = findClose.call(handle)
			# Return the data
			return nil if (handle == WinEx::INVALID_HANDLE_VALUE)
			return win32_find_data
		end
		
		# Returns +file_name+ with all forward slashes converted to backward slashes.
		# * +file_name+ --- UTF-8 +String+
		def self.normalize_file_name(file_name)
			return file_name.gsub(%[/], '\\')
		end
		
		# Returns +file_name+ normalized and in UTF-16 NULL terminated.
		# * +file_name+ --- UTF-8 +String+
		def self.normalize_w(file_name)
			file_name = self.normalize_file_name(file_name)
			return UnicodeEx::UTF8::to_w(file_name) + "\000\000"
		end
	end
		
	end # IS_PC
end

# Windows spesific addons
if WinEx::IS_PC
module UnicodeEx
	
	class UTF8 < String	
		# Returns an UTF-16 +String+.
		#  text = UnicodeEx::UTF8.new('Hello')
		#  utf16 = text.to_w
		def to_w()
			# Declare API calls
			multiByteToWideChar = Win32API.new('Kernel32','MultiByteToWideChar',['I','L','P','I','P','I'], 'I')
			# Get the buffer size
			buffer_size = multiByteToWideChar.call(WinEx::CP_UTF8, 0, self, self.bytes, nil, 0)
			# Reserve memory for the return string
			utf16_string = '  ' * buffer_size
			# Make the call
			ret = multiByteToWideChar.call(WinEx::CP_UTF8, 0, self, self.bytes, utf16_string, self.length)
			return utf16_string
		end
		
		# Returns an UTF-16 +String+.
		# * +file_name+ --- UTF-8 +String+
		#  utf16 = UnicodeEx::UTF8.to_w('Hello')
		def self.to_w(utf8_string)
			#return Unicode::UTF8.new(utf8_string).to_w
			return UTF8.new(utf8_string).to_w
		end
		
		# Returns an UTF-8 string from an UTF-16 string.
		# * +utf16_string+ --- UTF-16 +String+
		def self.from_w(utf16_string)
			# (!)
		end
	end
	
end # module UnicodeEx
end # IS_PC